package com.medistat.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.junit.Assert;

import com.medistat.model.Schedule;
import com.medistat.model.Work;
import com.medistat.model.enums.Days;

public abstract class DateUtils {
	
	@SuppressWarnings("deprecation")
	public static List<Work> generateSevenDaysFrom(Date date, Schedule schedule) {
		if(date==null || schedule==null) {
			return null;
		}
		
		List<Work> result = new ArrayList<Work>();
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(date.getYear()+1900, date.getMonth(), date.getDate());
		
		for(int i=0; i<7; i++) {
			Work dayInWeek = ObjectFactory.create(Work.class);
			calendar.add(Calendar.DATE,i);
			dayInWeek.setDate(calendar);
			dayInWeek.setSchedule(schedule);
			result.add(dayInWeek);
			calendar.add(Calendar.DATE,-i);
		} 
		return result;
	}

	public static Integer getWeekFromDate(Calendar date) {
		if(date==null) {
			return null;
		}
		return date.get(Calendar.WEEK_OF_YEAR);
	}

	public static Integer getYearFromDate(Calendar date) {
		if(date==null) {
			return null;
		}
		return date.get(Calendar.YEAR);
	}
	
	public static Integer getMaximumDayInMonthFromDate(Calendar date) {
		if(date==null) {
			return null;
		}
		return date.getActualMaximum(Calendar.DAY_OF_MONTH);
	}
	
	public static Integer getMinimumDayInMonthFromDate(Calendar date) {
		if(date==null) {
			return null;
		}
		return date.getActualMinimum(Calendar.DAY_OF_MONTH);
	}

	public static Integer getMonthFromDate(Calendar date) {
		if(date==null) {
			return null;
		}
		return date.get(Calendar.MONTH);
	}

	public static Calendar composeDate(Integer year, Integer month, Integer day) {
		if(year==null  || month==null || day==null) {
			return null;
		}
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(year, month, day);
		return calendar;
	}

	@SuppressWarnings("unchecked")
	public static List<Calendar> generateSevenDatesStartingWith(Calendar date) {
		if(date==null) {
			return Collections.EMPTY_LIST;
		}
		List<Calendar> dates = new ArrayList<Calendar>();
		Calendar currentDate = date;
		dates.add(currentDate);
		for(int i = 0; i<6; i++) {
			Calendar nextDate = getNextDateFrom(currentDate);
			dates.add(nextDate);
			currentDate = nextDate;
		}
		return dates;
	}
	
	public static Calendar getNextDateFrom(Calendar date) {
		if(date==null) {
			return null;
		}
		Calendar result = Calendar.getInstance();
		result.clear();
		result.set(date.get(Calendar.YEAR), date.get(Calendar.MONTH), date.get(Calendar.DATE)+1);
		return result;
	}

	public static Calendar getFirstDateOfFirstWeekInThisMonth(Calendar date) {
		Assert.assertNotNull(date);
		if(date.get(Calendar.DATE)!=1) {
			date.set(date.get(Calendar.YEAR),date.get(Calendar.MONTH), 1);
		}
		int dayInWeek = date.get(Calendar.DAY_OF_WEEK);
		if(dayInWeek==1) {
			dayInWeek=8;
		}
		Calendar result = Calendar.getInstance();
		result.clear();
		result.set(date.get(Calendar.YEAR),date.get(Calendar.MONTH), (date.get(Calendar.DATE) - dayInWeek + 2));
		return result;
	}

	public static Calendar getMinDate(Calendar date) {
		Assert.assertNotNull(date);
		int minDateInMonth = getMinimumDayInMonthFromDate(date);
		
		Calendar minDate = Calendar.getInstance();
		minDate.clear();
		minDate.set(date.get(Calendar.YEAR),date.get(Calendar.MONTH), minDateInMonth);
		return minDate;
	}
	
	public static Date getMinDate(Date date) {
		Assert.assertNotNull(date);
		Calendar calendar = getMinDate(convertDateToCalendar(date));
		return convertCalendarToDate(calendar);
	}
	//TODO: Write tests
	public static Calendar getMaxDate(Calendar date) {
		Assert.assertNotNull(date);
		int maxDateInMonth = getMaximumDayInMonthFromDate(date);
		
		Calendar maxDate = Calendar.getInstance();
		maxDate.clear();
		maxDate.set(date.get(Calendar.YEAR),date.get(Calendar.MONTH), maxDateInMonth);
		return maxDate;
	}
	//TODO: Write tests
	public static Date getMaxDate(Date date) {
		Assert.assertNotNull(date);
		Calendar calendar = getMaxDate(convertDateToCalendar(date));
		return convertCalendarToDate(calendar);
	}
	//TODO: Write tests
	public static Integer getNumberOfWeeksInMonth(Calendar date) {
		Assert.assertNotNull(date);
		Calendar maxDate = getMaxDate(date);
		return maxDate.get(Calendar.WEEK_OF_MONTH);
	}
	//TODO: Write tests
	@SuppressWarnings("deprecation")
	public static Calendar convertDateToCalendar(Date date) {
		Assert.assertNotNull("Datum mora biti vpisan !", date);
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.set(date.getYear()+1900, date.getMonth(), date.getDate());
		return calendar;
	}
	
	//TODO: Write tests
	public static Date convertCalendarToDate(Calendar calendar) {
		Assert.assertNotNull(calendar);
		return calendar.getTime();
	}
	
	//TODO: Write tests
	public static Calendar convertStringToCalendar(String dateUntilFromMap) {
		Assert.assertNotNull("Input string in method 'convertStringToCalendar' in DateUtils class should not be null", dateUntilFromMap);
		String []array = dateUntilFromMap.split("-");
		Calendar result = Calendar.getInstance();
		result.clear();
		result.set(Integer.valueOf(array[2]), Integer.valueOf(array[1]), Integer.valueOf(array[0]));
		return result;
	}

	
	//TODO: Write tests
	public static String convertCalendarToStringDate(Calendar calendar) {
		Assert.assertNotNull(calendar);
		Date date = calendar.getTime();
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(normalizeDate(date.getDate()));
		stringBuilder.append("-");
		stringBuilder.append(normalizeMonth(date.getMonth()+1));
		stringBuilder.append("-");
		stringBuilder.append(date.getYear() + 1900);
		return stringBuilder.toString();
	}
	//TODO: Write tests
		public static String convertCalendarToStringDateYearFirst(Calendar calendar) {
			Assert.assertNotNull(calendar);
			Date date = calendar.getTime();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.append(date.getYear() + 1900);
			stringBuilder.append("-");
			stringBuilder.append(normalizeMonth(date.getMonth()+1));
			stringBuilder.append("-");
			stringBuilder.append(normalizeDate(date.getDate()));
			return stringBuilder.toString();
		}
	
	private static String normalizeMonth(int month) {
		if(month < 10) {
			 return "0" + month;
		}
		return String.valueOf(month);
	}
	
	private static String normalizeDate(int date) {
		if(date < 10) {
			 return "0" + date;
		}
		return String.valueOf(date);
	}

	public static Days getDayFrom(Calendar date) {
		Assert.assertNotNull(date);
		Integer dayInWeek = date.get(Calendar.DAY_OF_WEEK);
		try {
			return convertToMedistatDayInWeek(dayInWeek);
		} catch (Exception e) {
		}
		return null;
	}

	protected static Days convertToMedistatDayInWeek(int dayInWeek) throws Exception {
		if(dayInWeek<1 || dayInWeek>7) {
			throw new Exception("day in week should not be less then 1 or higher then 7");
		} 
		if(dayInWeek==1) {
			dayInWeek=8;
		}
		return Days.values()[dayInWeek-2];
	}

	public static Integer getWeekInMonthFrom(Calendar date) {
		Assert.assertNotNull(date);
		return date.get(Calendar.WEEK_OF_MONTH);
	}
	
	public static Integer getWeekInYearFrom(Calendar date) {
		Assert.assertNotNull(date);
		return date.get(Calendar.WEEK_OF_YEAR);
	}

	public static boolean isDateFromThisMonth(Calendar date, Integer month) {
		Assert.assertNotNull(date);
		Assert.assertNotNull(month);
		return getMonthFromDate(date).equals(month);
	}

	public static boolean isWeekend(Days day) {
		Assert.assertNotNull("Input day in method isWeekend from DateUtils class should not be empty", day);
		return Days.SATURDAY.equals(day) || Days.SUNDAY.equals(day);
	}
	
	public static boolean isWorkingWeek(Days day) {
		Assert.assertNotNull("Input day in method isWeekend from DateUtils class should not be empty", day);
		return Days.MONDAY.equals(day) || Days.TUESDAY.equals(day) || Days.WEDNESDAY.equals(day) || Days.THURSDAY.equals(day) || Days.FRIDAY.equals(day);
	}
	
	public static boolean isFriday(Days day) {
		Assert.assertNotNull("Input day in method isFriday from DateUtils class should not be empty", day);
		return Days.FRIDAY.equals(day);
	}

	public static boolean isSaturday(Days day) {
		Assert.assertNotNull("Input day in method isSaturday from DateUtils class should not be empty", day);
		return Days.SATURDAY.equals(day);
	}
	
	public static boolean isSunday(Days day) {
		Assert.assertNotNull("Input day in method isSunday from DateUtils class should not be empty", day);
		return Days.SUNDAY.equals(day);
	}
	
	public static List<Calendar> getDatesFor(Integer year, Integer month) {
		Calendar date = Calendar.getInstance();
		date.clear();
		date.set(year, month-1, 1);
		List<Calendar> dates = new ArrayList<Calendar>();
		int firstDay = 1;
		do {
			Calendar clone = (Calendar) date.clone();
			clone.set(date.get(Calendar.YEAR), date.get(Calendar.MONTH), date.get(Calendar.DAY_OF_MONTH) + 1);
			dates.add(clone);
			date=clone;
		}
		while(date.get(Calendar.DAY_OF_MONTH) > firstDay);
		return dates;
		
	}

	public static List<Integer> getWeeksFor(Integer year, Integer month) {
		Calendar date = Calendar.getInstance();
		date.clear();
		date.set(year, month-1, 1);
		List<Integer> weeks = new ArrayList<Integer>(); 
		do {
			weeks.add(date.get(Calendar.WEEK_OF_YEAR));
			date.add(Calendar.WEEK_OF_YEAR, 1);
			
		} while(date.get(Calendar.MONTH) == month-1);
		return weeks;
	}
	
	public static Calendar addDate(Calendar date, int amount) {
		Assert.assertNotNull("Input date in method addDate from DateUtils class should not be empty", date);
		Calendar newDate = (Calendar) date.clone();
		newDate.add(Calendar.DATE, amount);
		return newDate;
	}
	
	public static Calendar subtractDate(Calendar date, int amount) {
		Assert.assertNotNull("Input date in method subtractDate from DateUtils class should not be empty", date);
		return addDate(date, -amount);
	}
	
	public static boolean isFirstThursdayInMonth(Calendar date) {
		Assert.assertNotNull("Input date in method isFirstThursdayInMonth from DateUtils class should not be empty", date);
		Days day = getDayFrom(date);
		if(!DaysUtils.isThursday(day) || date.get(Calendar.DATE) > 7) {
			return false;
		} else {
			Calendar firstDate = getMinDate(date);
			Days firstDay = getDayFrom(firstDate);
			while(!firstDay.equals(Days.THURSDAY)) {
				firstDate = DateUtils.addDate(firstDate, 1);
				firstDay = getDayFrom(firstDate);
			}
			return date.equals(firstDate);
		}
	}
}
